home *** CD-ROM | disk | FTP | other *** search
/ Personal Computer World 2008 February / PCWFEB08.iso / Software / Freeware / Miro 1.0 / Miro_Installer.exe / xulrunner / chrome / toolkit.jar / content / global / contentAreaUtils.js < prev    next >
Encoding:
JavaScript  |  2006-04-25  |  32.5 KB  |  934 lines

  1. //@line 40 "/c/mozilla/toolkit/content/contentAreaUtils.js"
  2.  
  3.  
  4. /**
  5.  * openNewTabWith: opens a new tab with the given URL.
  6.  *
  7.  * @param href The URL to open (as a string).
  8.  * @param sourceURL The URL of the document from which the URL came, or null.
  9.  *          This is used to set the referrer header and to do a security check of whether
  10.  *          the document as allowed to reference the URL.
  11.  *          If null, there will be no referrer header and no security check.
  12.  * @param postData Form POST data, or null.
  13.  * @param event The triggering event (for the purpose of determining whether to open in the background), or null
  14.  */ 
  15. function openNewTabWith(href, sourceURL, postData, event)
  16. {
  17.   if (sourceURL)
  18.     urlSecurityCheck(href, sourceURL);
  19.  
  20.   var prefSvc = Components.classes["@mozilla.org/preferences-service;1"]
  21.                           .getService(Components.interfaces.nsIPrefService);
  22.   prefSvc = prefSvc.getBranch(null);
  23.  
  24.   // should we open it in a new tab?
  25.   var loadInBackground = true;
  26.   try {
  27.     loadInBackground = prefSvc.getBoolPref("browser.tabs.loadInBackground");
  28.   }
  29.   catch(ex) {
  30.   }
  31.  
  32.   if (event && event.shiftKey)
  33.     loadInBackground = !loadInBackground;
  34.  
  35.   // As in openNewWindowWith(), we want to pass the charset of the
  36.   // current document over to a new tab. 
  37.   var wintype = document.firstChild.getAttribute('windowtype');
  38.   var originCharset;
  39.   if (wintype == "navigator:browser")
  40.     originCharset = window.content.document.characterSet;
  41.  
  42.   // open link in new tab
  43.   var browser = top.document.getElementById("content");
  44.  
  45.   var referrerURI = sourceURL ? makeURI(sourceURL) : null;
  46.  
  47.   browser.loadOneTab(href, referrerURI, originCharset, postData, loadInBackground);
  48. }
  49.  
  50. function openNewWindowWith(href, sourceURL, postData)
  51. {
  52.   if (sourceURL)
  53.     urlSecurityCheck(href, sourceURL);
  54.  
  55.   // if and only if the current window is a browser window and it has a document with a character
  56.   // set, then extract the current charset menu setting from the current document and use it to
  57.   // initialize the new browser window...
  58.   var charsetArg = null;
  59.   var wintype = document.firstChild.getAttribute('windowtype');
  60.   if (wintype == "navigator:browser")
  61.     charsetArg = "charset=" + window.content.document.characterSet;
  62.  
  63.   var referrerURI = sourceURL ? makeURI(sourceURL) : null;
  64.  
  65.   window.openDialog(getBrowserURL(), "_blank", "chrome,all,dialog=no", href, charsetArg, referrerURI, postData);
  66. }
  67.  
  68. /**
  69.  * urlSecurityCheck: JavaScript wrapper for CheckLoadURI.
  70.  * If |sourceURL| is not allowed to link to |url|, this function throws with an error message.
  71.  *
  72.  * @param url The URL a page has linked to.
  73.  * @param sourceURL The URL of the document from which the URL came.
  74.  */
  75. function urlSecurityCheck(url, sourceURL)
  76. {
  77.   const nsIScriptSecurityManager = Components.interfaces.nsIScriptSecurityManager;
  78.   var secMan = Components.classes["@mozilla.org/scriptsecuritymanager;1"]
  79.                          .getService(nsIScriptSecurityManager);
  80.   try {
  81.     secMan.checkLoadURIStr(sourceURL, url, nsIScriptSecurityManager.STANDARD);
  82.   } catch (e) {
  83.     throw "Load of " + url + " from " + sourceURL + " denied.";
  84.   }
  85. }
  86.  
  87. function webPanelSecurityCheck(aSourceURL, aDestURL) {
  88.   var sourceURI = makeURI(aSourceURL);
  89.   var destURI = makeURI(aDestURL);
  90.  
  91.   const nsIScriptSecurityManager = Components.interfaces.nsIScriptSecurityManager;
  92.   var secMan = Components.classes["@mozilla.org/scriptsecuritymanager;1"]
  93.                          .getService(nsIScriptSecurityManager);
  94.   try {
  95.     secMan.checkLoadURI(sourceURI, destURI, nsIScriptSecurityManager.STANDARD);
  96.   } catch (e) {
  97.     return false;
  98.   }
  99.   return true;
  100. }
  101.  
  102. function isContentFrame(aFocusedWindow)
  103. {
  104.   if (!aFocusedWindow)
  105.     return false;
  106.  
  107.   return (aFocusedWindow.top == window.content);
  108. }
  109.  
  110.  
  111. const kSaveAsType_Complete = 0;   // Save document with attached objects
  112. // const kSaveAsType_URL = 1;     // Save document or URL by itself
  113. const kSaveAsType_Text = 2;       // Save document, converting to plain text. 
  114.  
  115. // Clientelle: (Make sure you don't break any of these)
  116. //  - File    ->  Save Page/Frame As...
  117. //  - Context ->  Save Page/Frame As...
  118. //  - Context ->  Save Link As...
  119. //  - Alt-Click links in web pages
  120. //  - Alt-Click links in the UI
  121. //
  122. // Try saving each of these types:
  123. // - A complete webpage using File->Save Page As, and Context->Save Page As
  124. // - A webpage as HTML only using the above methods
  125. // - A webpage as Text only using the above methods
  126. // - An image with an extension (e.g. .jpg) in its file name, using
  127. //   Context->Save Image As...
  128. // - An image without an extension (e.g. a banner ad on cnn.com) using
  129. //   the above method. 
  130. // - A linked document using Save Link As...
  131. // - A linked document using Alt-click Save Link As...
  132. //
  133. function saveURL(aURL, aFileName, aFilePickerTitleKey, aShouldBypassCache,
  134.                  aSkipPrompt, aReferrer)
  135. {
  136.   internalSave(aURL, null, aFileName, null, null, aShouldBypassCache,
  137.                aFilePickerTitleKey, null, aReferrer, aSkipPrompt);
  138. }
  139.  
  140. // Just like saveURL, but will get some info off the image before
  141. // calling internalSave
  142. // Clientelle: (Make sure you don't break any of these)
  143. //  - Context ->  Save Image As...
  144. const imgICache = Components.interfaces.imgICache;
  145. const nsISupportsCString = Components.interfaces.nsISupportsCString;
  146.  
  147. function saveImageURL(aURL, aFileName, aFilePickerTitleKey, aShouldBypassCache,
  148.                       aSkipPrompt, aReferrer)
  149. {
  150.   var contentType = null;
  151.   var contentDisposition = null;
  152.   if (!aShouldBypassCache) {
  153.     try {
  154.       var imageCache = Components.classes["@mozilla.org/image/cache;1"]
  155.                                  .getService(imgICache);
  156.       var props =
  157.         imageCache.findEntryProperties(makeURI(aURL, getCharsetforSave(null)));
  158.       if (props) {
  159.         contentType = props.get("type", nsISupportsCString);
  160.         contentDisposition = props.get("content-disposition",
  161.                                        nsISupportsCString);
  162.       }
  163.     } catch (e) {
  164.       // Failure to get type and content-disposition off the image is non-fatal
  165.     }
  166.   }
  167.   internalSave(aURL, null, aFileName, contentDisposition, contentType,
  168.                aShouldBypassCache, aFilePickerTitleKey, null, aReferrer, aSkipPrompt);
  169. }
  170.  
  171. function saveFrameDocument()
  172. {
  173.   var focusedWindow = document.commandDispatcher.focusedWindow;
  174.   if (isContentFrame(focusedWindow))
  175.     saveDocument(focusedWindow.document);
  176. }
  177.  
  178. function saveDocument(aDocument, aSkipPrompt)
  179. {
  180.   if (!aDocument)
  181.     throw "Must have a document when calling saveDocument";
  182.  
  183.   // We want to use cached data because the document is currently visible.
  184.   var contentDisposition = null;
  185.   try {
  186.     contentDisposition =
  187.       aDocument.defaultView
  188.                .QueryInterface(Components.interfaces.nsIInterfaceRequestor)
  189.                .getInterface(Components.interfaces.nsIDOMWindowUtils)
  190.                .getDocumentMetadata("content-disposition");
  191.   } catch (ex) {
  192.     // Failure to get a content-disposition is ok
  193.   }
  194.   internalSave(aDocument.location.href, aDocument, null, contentDisposition,
  195.                aDocument.contentType, false, null, null, aSkipPrompt);
  196. }
  197.  
  198. /**
  199.  * internalSave: Used when saving a document or URL. This method:
  200.  *  - Determines a local target filename to use (unless parameter
  201.  *    aChosenData is non-null)
  202.  *  - Determines content-type if possible
  203.  *  - Prompts the user to confirm the destination filename and save mode
  204.  *    (content-type affects this)
  205.  *  - Creates a 'Persist' object (which will perform the saving in the
  206.  *    background) and then starts it.
  207.  *
  208.  * @param aURL The String representation of the URL of the document being saved
  209.  * @param aDocument The document to be saved
  210.  * @param aDefaultFileName The caller-provided suggested filename if we don't
  211.  *        find a better one
  212.  * @param aContentDisposition The caller-provided content-disposition header
  213.  *         to use.
  214.  * @param aContentType The caller-provided content-type to use
  215.  * @param aShouldBypassCache If true, the document will always be refetched
  216.  *        from the server
  217.  * @param aFilePickerTitleKey Alternate title for the file picker
  218.  * @param aChosenData If non-null this contains an instance of object AutoChosen
  219.  *        (see below) which holds pre-determined data so that the user does not
  220.  *        need to be prompted for a target filename.
  221.  * @param aReferrer the referrer URI object (not URL string) to use, or null
  222.           if no referrer should be sent.
  223.  * @param aSkipPrompt If true, the file will be saved to the default download folder.
  224.  */
  225. function internalSave(aURL, aDocument, aDefaultFileName, aContentDisposition,
  226.                       aContentType, aShouldBypassCache, aFilePickerTitleKey,
  227.                       aChosenData, aReferrer, aSkipPrompt)
  228. {
  229.   if (aSkipPrompt == undefined)
  230.     aSkipPrompt = false;
  231.  
  232.   // Note: aDocument == null when this code is used by save-link-as...
  233.   var saveMode = GetSaveModeForContentType(aContentType);
  234.   var isDocument = aDocument != null && saveMode != SAVEMODE_FILEONLY;
  235.   var saveAsType = kSaveAsType_Complete;
  236.  
  237.   var file, fileURL;
  238.   // Find the URI object for aURL and the FileName/Extension to use when saving.
  239.   // FileName/Extension will be ignored if aChosenData supplied.
  240.   var fileInfo = new FileInfo(aDefaultFileName);
  241.   if (aChosenData)
  242.     file = aChosenData.file;
  243.   else {
  244.     var charset = null;
  245.     if (aDocument)
  246.       charset = aDocument.characterSet;
  247.     else if (aReferrer)
  248.       charset = aReferrer.originCharset;
  249.     initFileInfo(fileInfo, aURL, aDocument, aContentType, aContentDisposition,
  250.                  charset);
  251.     var fpParams = {
  252.       fpTitleKey: aFilePickerTitleKey,
  253.       isDocument: isDocument,
  254.       fileInfo: fileInfo,
  255.       contentType: aContentType,
  256.       saveMode: saveMode,
  257.       saveAsType: saveAsType,
  258.       file: file,
  259.       fileURL: fileURL
  260.     };
  261.  
  262.     if (!getTargetFile(fpParams, aSkipPrompt))
  263.       // If the method returned false this is because the user cancelled from
  264.       // the save file picker dialog.
  265.       return;
  266.  
  267.     saveAsType = fpParams.saveAsType;
  268.     saveMode = fpParams.saveMode;
  269.     file = fpParams.file;
  270.     fileURL = fpParams.fileURL;
  271.   }
  272.  
  273.   if (!fileURL)
  274.     fileURL = makeFileURI(file);
  275.  
  276.   // XXX We depend on the following holding true in appendFiltersForContentType():
  277.   // If we should save as a complete page, the saveAsType is kSaveAsType_Complete.
  278.   // If we should save as text, the saveAsType is kSaveAsType_Text.
  279.   var useSaveDocument = isDocument &&
  280.                         (((saveMode & SAVEMODE_COMPLETE_DOM) && (saveAsType == kSaveAsType_Complete)) ||
  281.                          ((saveMode & SAVEMODE_COMPLETE_TEXT) && (saveAsType == kSaveAsType_Text)));
  282.   // If we're saving a document, and are saving either in complete mode or
  283.   // as converted text, pass the document to the web browser persist component.
  284.   // If we're just saving the HTML (second option in the list), send only the URI.
  285.   var source = useSaveDocument ? aDocument : fileInfo.uri;
  286.   var persistArgs = {
  287.     source      : source,
  288.     contentType : (!aChosenData && useSaveDocument &&
  289.                    saveAsType == kSaveAsType_Text) ?
  290.                    "text/plain" : aContentType,
  291.     target      : fileURL,
  292.     postData    : isDocument ? getPostData() : null,
  293.     bypassCache : aShouldBypassCache
  294.   };
  295.  
  296.   var persist = makeWebBrowserPersist();
  297.  
  298.   // Calculate persist flags.
  299.   const nsIWBP = Components.interfaces.nsIWebBrowserPersist;
  300.   const flags = nsIWBP.PERSIST_FLAGS_REPLACE_EXISTING_FILES;
  301.   if (aShouldBypassCache)
  302.     persist.persistFlags = flags | nsIWBP.PERSIST_FLAGS_BYPASS_CACHE;
  303.   else
  304.     persist.persistFlags = flags | nsIWBP.PERSIST_FLAGS_FROM_CACHE;
  305.  
  306.   // Leave it to WebBrowserPersist to discover the encoding type (or lack thereof):
  307.   persist.persistFlags |= nsIWBP.PERSIST_FLAGS_AUTODETECT_APPLY_CONVERSION;
  308.  
  309.   // Create download and initiate it (below)
  310.   var tr = Components.classes["@mozilla.org/transfer;1"].createInstance(Components.interfaces.nsITransfer);
  311.  
  312.   if (useSaveDocument) {
  313.     // Saving a Document, not a URI:
  314.     var filesFolder = null;
  315.     if (persistArgs.contentType != "text/plain") {
  316.       // Create the local directory into which to save associated files.
  317.       filesFolder = file.clone();
  318.  
  319.       var nameWithoutExtension = filesFolder.leafName.replace(/\.[^.]*$/, "");
  320.       var filesFolderLeafName = getStringBundle().formatStringFromName("filesFolder",
  321.                                                                        [nameWithoutExtension],
  322.                                                                        1);
  323.  
  324.       filesFolder.leafName = filesFolderLeafName;
  325.     }
  326.  
  327.     var encodingFlags = 0;
  328.     if (persistArgs.contentType == "text/plain") {
  329.       encodingFlags |= nsIWBP.ENCODE_FLAGS_FORMATTED;
  330.       encodingFlags |= nsIWBP.ENCODE_FLAGS_ABSOLUTE_LINKS;
  331.       encodingFlags |= nsIWBP.ENCODE_FLAGS_NOFRAMES_CONTENT;
  332.     }
  333.     else {
  334.       encodingFlags |= nsIWBP.ENCODE_FLAGS_ENCODE_BASIC_ENTITIES;
  335.     }
  336.  
  337.     const kWrapColumn = 80;
  338.     tr.init((aChosenData ? aChosenData.uri : fileInfo.uri),
  339.             persistArgs.target, "", null, null, null, persist);
  340.     persist.progressListener = tr;
  341.     persist.saveDocument(persistArgs.source, persistArgs.target, filesFolder,
  342.                          persistArgs.contentType, encodingFlags, kWrapColumn);
  343.   } else {
  344.     tr.init((aChosenData ? aChosenData.uri : source),
  345.             persistArgs.target, "", null, null, null, persist);
  346.     persist.progressListener = tr;
  347.     persist.saveURI((aChosenData ? aChosenData.uri : source),
  348.                     null, aReferrer, persistArgs.postData, null,
  349.                     persistArgs.target);
  350.   }
  351. }
  352.  
  353. /**
  354.  * Structure for holding info about automatically supplied parameters for
  355.  * internalSave(...). This allows parameters to be supplied so the user does not
  356.  * need to be prompted for file info.
  357.  * @param aFileAutoChosen This is an nsILocalFile object that has been
  358.  *        pre-determined as the filename for the target to save to
  359.  * @param aUriAutoChosen  This is the nsIURI object for the target
  360.  */
  361. function AutoChosen(aFileAutoChosen, aUriAutoChosen) {
  362.   this.file = aFileAutoChosen;
  363.   this.uri  = aUriAutoChosen;
  364. }
  365.  
  366. /**
  367.  * Structure for holding info about a URL and the target filename it should be
  368.  * saved to. This structure is populated by initFileInfo(...).
  369.  * @param aSuggestedFileName This is used by initFileInfo(...) when it
  370.  *        cannot 'discover' the filename from the url 
  371.  * @param aFileName The target filename
  372.  * @param aFileBaseName The filename without the file extension
  373.  * @param aFileExt The extension of the filename
  374.  * @param aUri An nsIURI object for the url that is being saved
  375.  */
  376. function FileInfo(aSuggestedFileName, aFileName, aFileBaseName, aFileExt, aUri) {
  377.   this.suggestedFileName = aSuggestedFileName;
  378.   this.fileName = aFileName;
  379.   this.fileBaseName = aFileBaseName;
  380.   this.fileExt = aFileExt;
  381.   this.uri = aUri;
  382. }
  383.  
  384. /**
  385.  * Determine what the 'default' filename string is, its file extension and the
  386.  * filename without the extension. This filename is used when prompting the user
  387.  * for confirmation in the file picker dialog.
  388.  * @param aFI A FileInfo structure into which we'll put the results of this method.
  389.  * @param aURL The String representation of the URL of the document being saved
  390.  * @param aDocument The document to be saved
  391.  * @param aContentType The content type we're saving, if it could be
  392.  *        determined by the caller.
  393.  * @param aContentDisposition The content-disposition header for the object
  394.  *        we're saving, if it could be determined by the caller.
  395.  * @param aURLCharset The charset of aURL.
  396.  */
  397. function initFileInfo(aFI, aURL, aDocument, aContentType, aContentDisposition,
  398.                       aURLCharset)
  399. {
  400.   if (!aURLCharset && aDocument && aDocument.characterSet)
  401.     aURLCharset = aDocument.characterSet;
  402.   try {
  403.     // Get an nsIURI object from aURL if possible:
  404.     try {
  405.       aFI.uri = makeURI(aURL, aURLCharset);
  406.       // Assuming nsiUri is valid, calling QueryInterface(...) on it will
  407.       // populate extra object fields (eg filename and file extension).
  408.       var url = aFI.uri.QueryInterface(Components.interfaces.nsIURL);
  409.       aFI.fileExt = url.fileExtension;
  410.     } catch (e) {
  411.     }
  412.  
  413.     // Get the default filename:
  414.     aFI.fileName = getDefaultFileName((aFI.suggestedFileName || aFI.fileName),
  415.                                       aFI.uri, aDocument, aContentDisposition);
  416.     // If aFI.fileExt is still blank, consider: aFI.suggestedFileName is supplied
  417.     // if saveURL(...) was the original caller (hence both aContentType and
  418.     // aDocument are blank). If they were saving a link to a website then make
  419.     // the extension .htm .
  420.     if (!aFI.fileExt && !aDocument && !aContentType && (/^http(s?):\/\//i.test(aURL))) {
  421.       aFI.fileExt = "htm";
  422.       aFI.fileBaseName = aFI.fileName;
  423.     } else {
  424.       aFI.fileExt = getDefaultExtension(aFI.fileName, aFI.uri, aContentType);
  425.       aFI.fileBaseName = getFileBaseName(aFI.fileName, aFI.fileExt);
  426.     }
  427.   } catch (e) {
  428.   }
  429. }
  430.  
  431. function getTargetFile(aFpP, aSkipPrompt)
  432. {
  433.   const prefSvcContractID = "@mozilla.org/preferences-service;1";
  434.   const prefSvcIID = Components.interfaces.nsIPrefService;                              
  435.   var prefs = Components.classes[prefSvcContractID]
  436.                         .getService(prefSvcIID).getBranch("browser.download.");
  437.  
  438.   const nsILocalFile = Components.interfaces.nsILocalFile;
  439.  
  440.   // ben 07/31/2003:
  441.   // |browser.download.defaultFolder| holds the default download folder for 
  442.   // all files when the user has elected to have all files automatically
  443.   // download to a folder. The values of |defaultFolder| can be either their
  444.   // desktop, their downloads folder (My Documents\My Downloads) or some other
  445.   // location of their choosing (which is mapped to |browser.download.dir|
  446.   // This pref is _unset_ when the user has elected to be asked about where
  447.   // to place every download - this will force the prompt to ask the user
  448.   // where to put saved files. 
  449.   var dir = null;
  450.   var useDownloadDir = prefs.getBoolPref("useDownloadDir");
  451.   
  452.   function getSpecialFolderKey(aFolderType) 
  453.   {
  454.     if (aFolderType == "Desktop")
  455.       return "Desk";
  456.     
  457.     if (aFolderType != "Downloads")
  458.       throw "ASSERTION FAILED: folder type should be 'Desktop' or 'Downloads'";
  459.     
  460. //@line 499 "/c/mozilla/toolkit/content/contentAreaUtils.js"
  461.     return "Pers";
  462. //@line 507 "/c/mozilla/toolkit/content/contentAreaUtils.js"
  463.   }
  464.   
  465.   function getDownloadsFolder(aFolder)
  466.   {
  467.     var fileLocator = Components.classes["@mozilla.org/file/directory_service;1"]
  468.                                 .getService(Components.interfaces.nsIProperties);
  469.     
  470.     var dir = fileLocator.get(getSpecialFolderKey(aFolder), Components.interfaces.nsILocalFile);
  471.     
  472.     var bundle = Components.classes["@mozilla.org/intl/stringbundle;1"]
  473.                            .getService(Components.interfaces.nsIStringBundleService);
  474.     bundle = bundle.createBundle("chrome://mozapps/locale/downloads/unknownContentType.properties");
  475.     
  476.     var description = bundle.GetStringFromName("myDownloads");
  477.     if (aFolder != "Desktop")
  478.       dir.append(description);
  479.     
  480.     return dir;
  481.   }
  482.   
  483.   switch (prefs.getIntPref("folderList")) {
  484.   case 0:
  485.     dir = getDownloadsFolder("Desktop")
  486.     break;
  487.   case 1:
  488.     dir = getDownloadsFolder("Downloads");
  489.     break;
  490.   case 2:
  491.     dir = prefs.getComplexValue("dir", nsILocalFile);
  492.     break;
  493.   }
  494.   
  495.   if (!aSkipPrompt || !useDownloadDir || !dir) {
  496.     // If we're asking the user where to save the file, root the Save As...
  497.     // dialog on they place they last picked. 
  498.     try {
  499.       dir = prefs.getComplexValue("lastDir", nsILocalFile);
  500.     }
  501.     catch (e) {
  502.       // No default download location. Default to desktop. 
  503.       var fileLocator = Components.classes["@mozilla.org/file/directory_service;1"]
  504.                                   .getService(Components.interfaces.nsIProperties);
  505.       
  506.       dir = fileLocator.get(getSpecialFolderKey("Desktop"), nsILocalFile);
  507.     }
  508.  
  509.     var fp = makeFilePicker();
  510.     var titleKey = aFpP.fpTitleKey || "SaveLinkTitle";
  511.     var bundle = getStringBundle();
  512.     fp.init(window, bundle.GetStringFromName(titleKey), 
  513.             Components.interfaces.nsIFilePicker.modeSave);
  514.     
  515.     fp.defaultExtension = aFpP.fileInfo.fileExt;
  516.     fp.defaultString = getNormalizedLeafName(aFpP.fileInfo.fileName,
  517.                                              aFpP.fileInfo.fileExt);
  518.     appendFiltersForContentType(fp, aFpP.contentType, aFpP.fileInfo.fileExt,
  519.                                 aFpP.saveMode);
  520.  
  521.     if (dir)
  522.       fp.displayDirectory = dir;
  523.     
  524.     if (aFpP.isDocument) {
  525.       try {
  526.         fp.filterIndex = prefs.getIntPref("save_converter_index");
  527.       }
  528.       catch (e) {
  529.       }
  530.     }
  531.   
  532.     fp.defaultExtension = aFpP.fileInfo.fileExt;
  533.     fp.defaultString = getNormalizedLeafName(aFpP.fileInfo.fileName,
  534.                                              aFpP.fileInfo.fileExt);
  535.   
  536.     if (fp.show() == Components.interfaces.nsIFilePicker.returnCancel || !fp.file)
  537.       return false;
  538.     
  539.     var directory = fp.file.parent.QueryInterface(nsILocalFile);
  540.     prefs.setComplexValue("lastDir", nsILocalFile, directory);
  541.  
  542.     fp.file.leafName = validateFileName(fp.file.leafName);
  543.     aFpP.saveAsType = fp.filterIndex;
  544.     aFpP.file = fp.file;
  545.     aFpP.fileURL = fp.fileURL;
  546.  
  547.     if (aFpP.isDocument)
  548.       prefs.setIntPref("save_converter_index", aFpP.saveAsType);
  549.   }
  550.   else {
  551.     dir.append(getNormalizedLeafName(aFpP.fileInfo.fileName,
  552.                                      aFpP.fileInfo.fileExt));
  553.     var file = dir;
  554.     
  555.     // Since we're automatically downloading, we don't get the file picker's 
  556.     // logic to check for existing files, so we need to do that here.
  557.     //
  558.     // Note - this code is identical to that in
  559.     //   mozilla/toolkit/mozapps/downloads/src/nsHelperAppDlg.js.in
  560.     // If you are updating this code, update that code too! We can't share code
  561.     // here since that code is called in a js component.
  562.     var collisionCount = 0;
  563.     while (file.exists()) {
  564.       collisionCount++;
  565.       if (collisionCount == 1) {
  566.         // Append "(2)" before the last dot in (or at the end of) the filename
  567.         // special case .ext.gz etc files so we don't wind up with .tar(2).gz
  568.         if (file.leafName.match(/\.[^\.]{1,3}\.(gz|bz2|Z)$/i))
  569.           file.leafName = file.leafName.replace(/\.[^\.]{1,3}\.(gz|bz2|Z)$/i, "(2)$&");
  570.         else
  571.           file.leafName = file.leafName.replace(/(\.[^\.]*)?$/, "(2)$&");
  572.       }
  573.       else {
  574.         // replace the last (n) in the filename with (n+1)
  575.         file.leafName = file.leafName.replace(/^(.*\()\d+\)/, "$1" + (collisionCount+1) + ")");
  576.       }
  577.     }
  578.     aFpP.file = file;
  579.   }
  580.  
  581.   return true;
  582. }
  583.  
  584. // We have no DOM, and can only save the URL as is.
  585. const SAVEMODE_FILEONLY      = 0x00;
  586. // We have a DOM and can save as complete.
  587. const SAVEMODE_COMPLETE_DOM  = 0x01;
  588. // We have a DOM which we can serialize as text.
  589. const SAVEMODE_COMPLETE_TEXT = 0x02;
  590.  
  591. // If we are able to save a complete DOM, the 'save as complete' filter
  592. // must be the first filter appended.  The 'save page only' counterpart
  593. // must be the second filter appended.  And the 'save as complete text'
  594. // filter must be the third filter appended.
  595. function appendFiltersForContentType(aFilePicker, aContentType, aFileExtension, aSaveMode)
  596. {
  597.   var bundle = getStringBundle();
  598.   // The bundle name for saving only a specific content type.
  599.   var bundleName;
  600.   // The corresponding filter string for a specific content type.
  601.   var filterString;
  602.  
  603.   // XXX all the cases that are handled explicitly here MUST be handled
  604.   // in GetSaveModeForContentType to return a non-fileonly filter.
  605.   switch (aContentType) {
  606.   case "text/html":
  607.     bundleName   = "WebPageHTMLOnlyFilter";
  608.     filterString = "*.htm; *.html";
  609.     break;
  610.  
  611.   case "application/xhtml+xml":
  612.     bundleName   = "WebPageXHTMLOnlyFilter";
  613.     filterString = "*.xht; *.xhtml";
  614.     break;
  615.  
  616.   case "text/xml":
  617.   case "application/xml":
  618.     bundleName   = "WebPageXMLOnlyFilter";
  619.     filterString = "*.xml";
  620.     break;
  621.  
  622.   default:
  623.     if (aSaveMode != SAVEMODE_FILEONLY)
  624.       throw "Invalid save mode for type '" + aContentType + "'";
  625.  
  626.     var mimeInfo = getMIMEInfoForType(aContentType, aFileExtension);
  627.     if (mimeInfo) {
  628.  
  629.       var extEnumerator = mimeInfo.getFileExtensions();
  630.  
  631.       var extString = "";
  632.       while (extEnumerator.hasMore()) {
  633.         var extension = extEnumerator.getNext();
  634.         if (extString)
  635.           extString += "; ";    // If adding more than one extension,
  636.                                 // separate by semi-colon
  637.         extString += "*." + extension;
  638.       }
  639.  
  640.       if (extString)
  641.         aFilePicker.appendFilter(mimeInfo.description, extString);
  642.     }
  643.  
  644.     break;
  645.   }
  646.  
  647.   if (aSaveMode & SAVEMODE_COMPLETE_DOM) {
  648.     aFilePicker.appendFilter(bundle.GetStringFromName("WebPageCompleteFilter"), filterString);
  649.     // We should always offer a choice to save document only if
  650.     // we allow saving as complete.
  651.     aFilePicker.appendFilter(bundle.GetStringFromName(bundleName), filterString);
  652.   }
  653.  
  654.   if (aSaveMode & SAVEMODE_COMPLETE_TEXT)
  655.     aFilePicker.appendFilters(Components.interfaces.nsIFilePicker.filterText);
  656.  
  657.   // Always append the all files (*) filter
  658.   aFilePicker.appendFilters(Components.interfaces.nsIFilePicker.filterAll);
  659. }
  660.  
  661.  
  662. function getPostData()
  663. {
  664.   try {
  665.     var sessionHistory = getWebNavigation().sessionHistory;
  666.     var entry = sessionHistory.getEntryAtIndex(sessionHistory.index, false);
  667.     entry = entry.QueryInterface(Components.interfaces.nsISHEntry);
  668.     return entry.postData;
  669.   }
  670.   catch (e) {
  671.   }
  672.   return null;
  673. }
  674.  
  675. function getStringBundle()
  676. {
  677.   const bundleURL = "chrome://global/locale/contentAreaCommands.properties";
  678.   
  679.   const sbsContractID = "@mozilla.org/intl/stringbundle;1";
  680.   const sbsIID = Components.interfaces.nsIStringBundleService;
  681.   const sbs = Components.classes[sbsContractID].getService(sbsIID);
  682.   
  683.   const lsContractID = "@mozilla.org/intl/nslocaleservice;1";
  684.   const lsIID = Components.interfaces.nsILocaleService;
  685.   const ls = Components.classes[lsContractID].getService(lsIID);
  686.   var appLocale = ls.getApplicationLocale();
  687.   return sbs.createBundle(bundleURL, appLocale);    
  688. }
  689.  
  690. function makeWebBrowserPersist()
  691. {
  692.   const persistContractID = "@mozilla.org/embedding/browser/nsWebBrowserPersist;1";
  693.   const persistIID = Components.interfaces.nsIWebBrowserPersist;
  694.   return Components.classes[persistContractID].createInstance(persistIID);
  695. }
  696.  
  697. function makeURI(aURL, aOriginCharset, aBaseURI)
  698. {
  699.   var ioService = Components.classes["@mozilla.org/network/io-service;1"]
  700.                             .getService(Components.interfaces.nsIIOService);
  701.   return ioService.newURI(aURL, aOriginCharset, aBaseURI);
  702. }
  703.  
  704. function makeFileURI(aFile)
  705. {
  706.   var ioService = Components.classes["@mozilla.org/network/io-service;1"]
  707.                             .getService(Components.interfaces.nsIIOService);
  708.   return ioService.newFileURI(aFile);
  709. }
  710.  
  711. function makeFilePicker()
  712. {
  713.   const fpContractID = "@mozilla.org/filepicker;1";
  714.   const fpIID = Components.interfaces.nsIFilePicker;
  715.   return Components.classes[fpContractID].createInstance(fpIID);
  716. }
  717.  
  718. function getMIMEService()
  719. {
  720.   const mimeSvcContractID = "@mozilla.org/mime;1";
  721.   const mimeSvcIID = Components.interfaces.nsIMIMEService;
  722.   const mimeSvc = Components.classes[mimeSvcContractID].getService(mimeSvcIID);
  723.   return mimeSvc;
  724. }
  725.  
  726. // Given aFileName, find the fileName without the extension on the end.
  727. function getFileBaseName(aFileName, aFileExt)
  728. {
  729.   // Remove the file extension from aFileName:
  730.   return aFileName.replace(/\.[^.]*$/, "");
  731. }
  732.  
  733. function getMIMETypeForURI(aURI)
  734. {
  735.   try {  
  736.     return getMIMEService().getTypeFromURI(aURI);
  737.   }
  738.   catch (e) {
  739.   }
  740.   return null;
  741. }
  742.  
  743. function getMIMEInfoForType(aMIMEType, aExtension)
  744. {
  745.   if (aMIMEType || aExtension) {
  746.     try {
  747.       return getMIMEService().getFromTypeAndExtension(aMIMEType, aExtension);
  748.     }
  749.     catch (e) {
  750.     }
  751.   }
  752.   return null;
  753. }
  754.  
  755. function getDefaultFileName(aDefaultFileName, aURI, aDocument,
  756.                             aContentDisposition)
  757. {
  758.   // 1) look for a filename in the content-disposition header, if any
  759.   if (aContentDisposition) {
  760.     const mhpContractID = "@mozilla.org/network/mime-hdrparam;1";
  761.     const mhpIID = Components.interfaces.nsIMIMEHeaderParam;
  762.     const mhp = Components.classes[mhpContractID].getService(mhpIID);
  763.     var dummy = { value: null };  // Need an out param...
  764.     var charset = getCharsetforSave(aDocument);
  765.  
  766.     var fileName = null;
  767.     try {
  768.       fileName = mhp.getParameter(aContentDisposition, "filename", charset,
  769.                                   true, dummy);
  770.     }
  771.     catch (e) {
  772.       try {
  773.         fileName = mhp.getParameter(aContentDisposition, "name", charset, true,
  774.                                     dummy);
  775.       }
  776.       catch (e) {
  777.       }
  778.     }
  779.     if (fileName)
  780.       return fileName;
  781.   }
  782.  
  783.   try {
  784.     var url = aURI.QueryInterface(Components.interfaces.nsIURL);
  785.     if (url.fileName != "") {
  786.       // 2) Use the actual file name, if present
  787.       var textToSubURI = Components.classes["@mozilla.org/intl/texttosuburi;1"]
  788.                                    .getService(Components.interfaces.nsITextToSubURI);
  789.       return validateFileName(textToSubURI.unEscapeURIForUI(url.originCharset || "UTF-8", url.fileName));
  790.     }
  791.   } catch (e) {
  792.     // This is something like a data: and so forth URI... no filename here.
  793.   }
  794.  
  795.   if (aDocument) {
  796.     var docTitle = validateFileName(aDocument.title).replace(/^\s+|\s+$/g, "");
  797.     if (docTitle) {
  798.       // 3) Use the document title
  799.       return docTitle;
  800.     }
  801.   }
  802.  
  803.   if (aDefaultFileName)
  804.     // 4) Use the caller-provided name, if any
  805.     return validateFileName(aDefaultFileName);
  806.  
  807.   // 5) If this is a directory, use the last directory name
  808.   var path = aURI.path.match(/\/([^\/]+)\/$/);
  809.   if (path && path.length > 1)
  810.     return validateFileName(path[1]);
  811.  
  812.   try {
  813.     if (aURI.host)
  814.       // 6) Use the host.
  815.       return aURI.host;
  816.   } catch (e) {
  817.     // Some files have no information at all, like Javascript generated pages
  818.   }
  819.   try {
  820.     // 7) Use the default file name
  821.     return getStringBundle().GetStringFromName("DefaultSaveFileName");
  822.   } catch (e) {
  823.     //in case localized string cannot be found
  824.   }
  825.   // 8) If all else fails, use "index"
  826.   return "index";
  827. }
  828.  
  829. function validateFileName(aFileName)
  830. {
  831.   var re = /[\/]+/g;
  832.   if (navigator.appVersion.indexOf("Windows") != -1) {
  833.     re = /[\\\/\|]+/g;
  834.     aFileName = aFileName.replace(/[\"]+/g, "'");
  835.     aFileName = aFileName.replace(/[\*\:\?]+/g, " ");
  836.     aFileName = aFileName.replace(/[\<]+/g, "(");
  837.     aFileName = aFileName.replace(/[\>]+/g, ")");
  838.   }
  839.   else if (navigator.appVersion.indexOf("Macintosh") != -1)
  840.     re = /[\:\/]+/g;
  841.   
  842.   return aFileName.replace(re, "_");
  843. }
  844.  
  845. function getNormalizedLeafName(aFile, aDefaultExtension)
  846. {
  847.   if (!aDefaultExtension)
  848.     return aFile;
  849.  
  850. //@line 895 "/c/mozilla/toolkit/content/contentAreaUtils.js"
  851.   // Remove trailing dots and spaces on windows
  852.   aFile = aFile.replace(/[\s.]+$/, "");
  853. //@line 898 "/c/mozilla/toolkit/content/contentAreaUtils.js"
  854.       
  855.   // Fix up the file name we're saving to to include the default extension
  856.   var i = aFile.lastIndexOf(".");
  857.   if (aFile.substr(i + 1) != aDefaultExtension)
  858.     return aFile + "." + aDefaultExtension;
  859.  
  860.   return aFile;
  861. }
  862.  
  863. function getDefaultExtension(aFilename, aURI, aContentType)
  864. {
  865.   if (aContentType == "text/plain" || aContentType == "application/octet-stream" || aURI.scheme == "ftp")
  866.     return "";   // temporary fix for bug 120327
  867.  
  868.   // First try the extension from the filename
  869.   const stdURLContractID = "@mozilla.org/network/standard-url;1";
  870.   const stdURLIID = Components.interfaces.nsIURL;
  871.   var url = Components.classes[stdURLContractID].createInstance(stdURLIID);
  872.   url.filePath = aFilename;
  873.  
  874.   var ext = url.fileExtension;
  875.  
  876.   // This mirrors some code in nsExternalHelperAppService::DoContent
  877.   // Use the filename first and then the URI if that fails
  878.   
  879.   var mimeInfo = getMIMEInfoForType(aContentType, ext);
  880.  
  881.   if (ext && mimeInfo && mimeInfo.extensionExists(ext))
  882.     return ext;
  883.   
  884.   // Well, that failed.  Now try the extension from the URI
  885.   var urlext;
  886.   try {
  887.     url = aURI.QueryInterface(Components.interfaces.nsIURL);
  888.     urlext = url.fileExtension;
  889.   } catch (e) {
  890.   }
  891.  
  892.   if (urlext && mimeInfo && mimeInfo.extensionExists(urlext)) {
  893.     return urlext;
  894.   }
  895.   else {
  896.     try {
  897.       return mimeInfo.primaryExtension;
  898.     }
  899.     catch (e) {
  900.       // Fall back on the extensions in the filename and URI for lack
  901.       // of anything better.
  902.       return ext || urlext;
  903.     }
  904.   }
  905. }
  906.  
  907. function GetSaveModeForContentType(aContentType)
  908. {
  909.   var saveMode = SAVEMODE_FILEONLY;
  910.   switch (aContentType) {
  911.   case "text/html":
  912.   case "application/xhtml+xml":
  913.     saveMode |= SAVEMODE_COMPLETE_TEXT;
  914.     // Fall through
  915.   case "text/xml":
  916.   case "application/xml":
  917.     saveMode |= SAVEMODE_COMPLETE_DOM;
  918.     break;
  919.   }
  920.  
  921.   return saveMode;
  922. }
  923.  
  924. function getCharsetforSave(aDocument)
  925. {
  926.   if (aDocument)
  927.     return aDocument.characterSet;
  928.  
  929.   if (document.commandDispatcher.focusedWindow)
  930.     return document.commandDispatcher.focusedWindow.document.characterSet;
  931.  
  932.   return window.content.document.characterSet;
  933. }
  934.